#!/usr/bin/env stackless

from system.networkNode import NetworkNode

class Server(NetworkNode):
    def __init__(self):
        NetworkNode.__init__(self)
        
        self.clients = set()
        self.varSetters = {}
        self.varGetters = {}
        self.state = {}
        self.systemCounts = {}
        self.systemClients = {}
        self.objectClients = {}
        self.actionClients = {}
        self.actionTypeCounts = {}
        self.rpcResults = {}
        self.subscriptions = {}
        self.failedActions = []
        
    def sendExcept(self, cmd, msg, dest, clientList = None):
        otherClients = filter(lambda c: c != dest, clientList or self.clients)
        for client in otherClients:
            self.send(cmd,msg,client)
        
    def receivePacket_getStateNames(self, msg, sender):
        self.send("stateNames",self.state.keys(),sender)
        
    def receivePacket_getState(self, msg, sender):
        for name, vars in self.state.iteritems():
            self.send("statePart",(name,vars),sender)
            
    def receivePacket_regetState(self, msg, sender):
        assert msg in self.state
        self.send("statePart",(msg,self.state[msg]),sender)
        
    def receivePacket_reg(self, msg, sender):
        if sender not in self.clients:
            self.log("got client", sender, msg)
            self.clients.add(sender)
            for system in msg:
                self.systemClients.setdefault(system,[]).append(sender)
            self.send("regack",None,sender)
            
            failedActions = self.failedActions
            self.failedActions = []
            for msg, sender in failedActions:
                self.receivePacket_runAction(msg,sender)
            
    def receivePacket_unreg(self, msg, sender):
        self.log("unreg", sender)
        self.send("unregack",None,sender)
        if sender in self.clients:
            self.log("lost client", sender)
            self.clients.remove(sender)
            
    def receivePacket_makeObject(self, msg, sender):
        name = self.makeObjectName(msg)
        #self.log("add object", name)
        self.objectClients[name] = sender
        self.state[name] = {}
        self.send("objectName",(msg,name),sender)
        
        self.makeObjectConfirmers = filter(lambda client: client != sender, self.clients)
        def dist():
            for client in self.makeObjectConfirmers:
                self.send("makeObject",name,client)
        dist()
        self.listenWait(lambda: self.makeObjectConfirmers, lambda: self.pump(['makeObjectAck']), dist)
        
    def receivePacket_makeObjectAck(self, msg, sender):
        if sender in self.makeObjectConfirmers:
            self.makeObjectConfirmers.remove(sender)
            
    def makeObjectName(self, baseName):
        if baseName not in self.state:
            return baseName
        else:
            i = 2
            name = "%s_%d" % (baseName,i)
            while name in self.state:
                i += 1
                name = "%s_%d" % (baseName,i)
            return name
            
    def receivePacket_deleteObject(self, msg, sender):
        assert msg in self.state, "cannot delete unknown object %s" % msg
        self.state.pop(msg)
        
        self.sendExcept("deleteObject",msg,sender)
        
    def receivePacket_distSetVar(self, msg, sender):
        self.sendExcept("setVar",msg,sender)
        
    def receivePacket_updateState(self, msg, sender):
        batchSets = {}
        for objectName, objectVars in msg.iteritems():
            for varName, val in objectVars.iteritems():
                assert objectName in self.state
                
                if varName in self.state.get(objectName,{}):
                    assert self.varSetters.setdefault((objectName,varName),sender) == sender
                    
                self.state[(objectName,varName)] = val
                
                for client in filter(lambda client: client != sender, self.varGetters.get((objectName,varName),())):
                    batchSets.setdefault(client,{}).setdefault(objectName,{})[varName] = val
                    
        for client, batchMsg in batchSets.iteritems():
            self.send("updateState",batchMsg,client)
        
    def receivePacket_setVar(self, msg, sender):
        #self.log("setVar", msg)
        
        objectName, varName, val = msg
        
        if self.state.get(objectName,{}).get(varName,None) != val:
            assert objectName in self.state, "cannot set var %s for unknown object %s" % (varName, objectName)
            
            objectId = (objectName, varName)
            
            if varName in self.state.get(objectName,{}):
                assert self.varSetters.setdefault(objectId,sender) == sender, \
                    "cannot have two clients set var %s in object %s" % (varName, objectName)
            
            self.state[objectName][varName] = val
            
            if objectId in self.varGetters:
                self.sendExcept("setVar",msg,sender,self.varGetters[objectId])
                #for client in self.varGetters[objectId]:
                    #self.send("setVar",msg,client)
                
    def receivePacket_requestVar(self, msg, sender):
        self.varGetters.setdefault(msg,set()).add(sender)
        self.send("requestVarAck",msg,sender)
        
    def makeActionName(self, actionType, actionName):
        self.actionTypeCounts.setdefault(actionType,0)
        name = "%s_%d" % (actionType, self.actionTypeCounts[actionType])
        self.actionTypeCounts[actionType] += 1
        return name
        
    def receivePacket_runAction(self, msg, sender):
        #self.log("receive_runAction",msg,sender)
        objectName, actionType, actionDesiredName, args = msg
        actionName = self.makeActionName(actionType, actionDesiredName)
        system = actionName.split('.')[0]
        if system not in self.systemClients:
            self.failedActions.append((msg,sender))
            return
        self.systemCounts.setdefault(system,0)
        runner = self.systemClients[system][self.systemCounts[system] % len(self.systemClients[system])]
        self.actionClients[actionName] = runner
        self.systemCounts[system] += 1
        self.send("runAction",(objectName,actionType,actionName,args),runner)
        self.send("actionName",(objectName,actionType,actionDesiredName,actionName),sender)
        
    def receivePacket_rpc(self, msg, sender):
        objectName, actionName, cmd, args = msg
        assert objectName in self.state and actionName in self.actionClients
        self.send("rpc",msg,self.actionClients[actionName])
        self.wait(lambda: not self.rpcResults.get((objectName,actionName,cmd),None), self.pump)
        self.send("rpcResult",(objectName,actionName,cmd,self.rpcResults[(objectName,actionName,cmd)].pop()), sender)
        
    def receivePacket_rpcResult(self, msg, sender):
        objectName, actionName, cmd, result = msg
        self.rpcResults.setdefault((objectName,actionName,cmd),[]).append(result)
        
    def receivePacket_subscribe(self, msg, sender):
        self.subscriptions.setdefault(msg,set()).add(sender)
        
    def receivePacket_unsubscribe(self, msg, sender):
        self.subscriptions[msg].remove(sender)
        
    def receivePacket_publish(self, msg, sender):
        cmd, args = msg
        if cmd in self.subscriptions:
            self.sendExcept("publish",msg,sender,self.subscriptions[cmd])
            
    def receivePacket_killNetwork(self, msg, sender):
        self.killNetwork()
        
    def killNetwork(self):
        def doKillNetwork():
            for client in self.clients:
                self.send("killNetwork",None,client)
        doKillNetwork()
        self.wait(lambda: self.clients, self.pump, doKillNetwork)
        self.killAll()
        
if __name__ == "__main__":
    server = Server()
    #server()
    #server.runAll()
    try:
        server()
        server.runAll()
    finally:
        server.killNetwork()
